package main

import (
	"fmt"
	"reflect"
)

func Display(name string, x interface{}) {
	fmt.Printf("Display %s (%T):\n", name, x)
	display(name, reflect.ValueOf(x))
}

func display(path string, v reflect.Value) {
	switch v.Kind() {
	case reflect.Invalid:
		fmt.Printf("%s = invalid\n", path)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
			display(fieldPath, v.Field(i))
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			display(fmt.Sprintf("%s[%s]", path, key), v.MapIndex(key))
		}
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			display(fmt.Sprintf("(*%s)", path), v.Elem())
		}
	case reflect.Interface:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
			display(path+".value", v.Elem())
		}
	default:
		fmt.Printf("%s = %v\n", path, v)
	}
}

type Person struct {
	Name    string
	age     int32
	friends []*Person
}

func main() {
	zoro := Person{
		Name: "zoro",
		age:  10,
	}
	luffy := Person{
		Name: "luffy",
		age:  18,
		friends: []*Person{
			&zoro,
		},
	}
	allen := Person{
		Name: "allen",
		age:  19,
		friends: []*Person{
			&luffy,
			&zoro,
		},
	}

	persons := map[string]Person{
		"allen": allen,
		"luffy": luffy,
		"zoro":  zoro,
	}

	Display("persons", persons)
}
